package logger

import (
	"fmt"
	"sync"
	"time"
)

type DefaultLogger struct {
	name   string
	level  Level
	output []StringWriter
	one    sync.Once
	do     func(*DefaultLogger)
}

func NewDefaultLogger(name string, do func(*DefaultLogger)) *DefaultLogger {
	return &DefaultLogger{name: name,
		level:  LevelDebug,
		output: []StringWriter{&NoopStringWriterImpl{}},
		do:     do,
	}
}

var Default = NewLogger("default")

func (l *DefaultLogger) Name() string {
	return l.name
}
func (l *DefaultLogger) Level() Level {
	return l.level
}

func (l *DefaultLogger) SetLevel(level Level) {
	l.level = level
}

func (l *DefaultLogger) TraceEnabled() bool {
	return l.level <= LevelTrace
}
func (l *DefaultLogger) DebugEnabled() bool {
	return l.level <= LevelDebug
}
func (l *DefaultLogger) InfoEnabled() bool {
	return l.level <= LevelInfo
}
func (l *DefaultLogger) WarnEnabled() bool {
	return l.level <= LevelWarn
}
func (l *DefaultLogger) ErrorEnabled() bool {
	return l.level <= LevelError
}

func (l *DefaultLogger) Trace(v ...interface{}) {
	if l.level > LevelTrace {
		return
	}
	l.logf(LevelTrace, nil, v...)
}

func (l *DefaultLogger) Debug(v ...interface{}) {
	if l.level > LevelDebug {
		return
	}
	l.logf(LevelDebug, nil, v...)
}

func (l *DefaultLogger) Info(v ...interface{}) {
	if l.level > LevelInfo {
		return
	}
	l.logf(LevelInfo, nil, v...)
}

func (l *DefaultLogger) Warn(v ...interface{}) {
	if l.level > LevelWarn {
		return
	}
	l.logf(LevelWarn, nil, v...)
}

func (l *DefaultLogger) Error(v ...interface{}) {
	if l.level > LevelError {
		return
	}
	l.logf(LevelError, nil, v...)
}

func (l *DefaultLogger) Tracef(format string, v ...interface{}) {
	if l.level > LevelTrace {
		return
	}
	l.logf(LevelTrace, &format, v...)
}

func (l *DefaultLogger) Debugf(format string, v ...interface{}) {
	if l.level > LevelDebug {
		return
	}
	l.logf(LevelDebug, &format, v...)
}

func (l *DefaultLogger) Infof(format string, v ...interface{}) {
	if l.level > LevelInfo {
		return
	}
	l.logf(LevelInfo, &format, v...)
}

func (l *DefaultLogger) Warnf(format string, v ...interface{}) {
	if l.level > LevelWarn {
		return
	}
	l.logf(LevelWarn, &format, v...)
}

func (l *DefaultLogger) Errorf(format string, v ...interface{}) {
	if l.level > LevelError {
		return
	}
	l.logf(LevelError, &format, v...)
}

func (l *DefaultLogger) logf(lv Level, format *string, v ...interface{}) {
	l.one.Do(func() {
		l.do(l)
	})
	msg := ""
	if format != nil {
		msg = fmt.Sprintf(*format, v...)
	} else {
		msg = fmt.Sprint(v...)
	}
	now := time.Now()
	for _, writer := range l.output {
		_, _ = writer.WriteString(now, l.name, lv.toString(), msg)
	}
}
